package cn.wolfcode.wolf.auth.interceptor;

import cn.wolfcode.wolf.auth.anno.RequireLogin;
import cn.wolfcode.wolf.auth.config.JwtProperties;
import cn.wolfcode.wolf.core.exception.BusinessException;


import cn.wolfcode.wolf.redis.utils.RedisCache;


import cn.wolfcode.wolf.user.redis.key.UserRedisKeyPrefix;
import cn.wolfcode.wolf.user.vo.LoginUser;
import io.jsonwebtoken.*;


import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.concurrent.TimeUnit;

@Slf4j
public class LoginInterceptor implements HandlerInterceptor {
    private final RedisCache redisCache;
    private final JwtProperties jwtProperties;
    public LoginInterceptor(RedisCache redisCache, JwtProperties jwtProperties) {
        this.redisCache = redisCache;
        this.jwtProperties = jwtProperties;
    }



    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 0.判断一个接口是否需要登陆拦截
        // 0.1 判断handler是否是HandlerMethod实例，如果不是，直接放行
        if (!(handler instanceof HandlerMethod)) {
            // handler =>静态资源 or handler =>CORS的预请求
            return true;
        }
        // 0.2 将handler对象转换为HandlerMethod对象
        HandlerMethod handlerMethod = (HandlerMethod) handler;
        // 0.3 从handlerMethod对象中获取对应的Controller对象
        Class<?> controllerClass = handlerMethod.getBeanType();
        // 0.4 分别从Controller和HandlerMethod上获取@Require注解，判断是否需要拦截
        RequireLogin classAnnotation = controllerClass.getAnnotation(RequireLogin.class);
        RequireLogin methodAnnotation = handlerMethod.getMethodAnnotation(RequireLogin.class);
        if (classAnnotation == null && methodAnnotation == null) {
            // 不需要拦截
            return true;
        }
        // 1.从请求头中拿到jwtToken
        String token = request.getHeader(LoginUser.TOKEN_HEADER);
        // 2.基于jwt sdk解析token，解析失败则抛出异常
        try {
            Jws<Claims> jwt = Jwts.parser().setSigningKey(jwtProperties.getSecret()).parseClaimsJws(token);
            // 3.获取token中登录时间信息，判断是否过期
            Claims claims = jwt.getBody();
//            // 确保 loginTime 和 expireTime 是 Long 类型
//            Long loginTime = convertToLong(claims.get("loginTime"));
//            Long expireTime = convertToLong(claims.get("expireTime"));
//
//            Long currentTime = System.currentTimeMillis();
//            Long min = (currentTime - loginTime) / 1000 / 60;
//            if (min > expireTime) {
//                // 4.如果已经过期，则抛出异常
//                throw new BusinessException("token已过期");
//            }
            String uuid =(String)claims.get(LoginUser.LOGIN_USER_REDIS_UUID);
            // 4.从redis中获取用户信息，如果用户不存在，则已过期
            String userLoginKey = UserRedisKeyPrefix.USERS_LOGIN_INFO_STRING.fullKey(uuid);
            LoginUser loginUser = redisCache.getCacheObject(userLoginKey);
            long loginTime;
            if (loginUser == null) {
                // 5.如果用户不存在，则抛出异常
                throw new BusinessException(401, "token已过期");
            }else if (loginUser.getExpireTime()-(loginTime=System.currentTimeMillis())<=LoginUser.TWENTY_MINUTES_MILLISECONDS) {
                // 6.如果用户过期时间小于20分钟，则刷新过期时间
                loginUser.setLoginTime(loginTime);
                long expireTime = loginTime+(jwtProperties.getExpireTime())*LoginUser.MINUTES_MILLISECONDS;
                loginUser.setExpireTime(expireTime);
                // 重新计算过期时间后，再次设置到redis中，覆盖原来的redis中的对象
                redisCache.setCacheObject(userLoginKey,loginUser,loginUser.getExpireTime(), TimeUnit.MINUTES);
            }

        } catch (Exception e) {
            log.warn("[登录拦截]，token解析失败");
            throw new BusinessException(401,"用户未认证"+"--"+"详细错误"+e.getMessage());
        }
        // 6.其他情况放行
        return true;
    }
    private Long convertToLong(Object value) {
        if (value instanceof Long) {
            return (Long) value;
        } else if (value instanceof Integer) {
            return ((Integer) value).longValue();
        } else {
            throw new ClassCastException("Unsupported type: " + value.getClass().getName());
        }
    }
}
